/**
 * 
 */
package jvs.peer.xmpp;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.net.Socket;
import java.text.ParseException;
import java.util.List;

import jvs.peer.io.ChannelManager;
import jvs.peer.io.File;
import jvs.peer.misc.CopyInputStream;
import jvs.peer.net.PeerSocket;
import android.os.Environment;

/**
 * @author qiangli
 *
 */
public class XmppFileUtil {

	public static final String CRLF = "\r\n";

	private static java.io.File cacheRoot = cacheRoot();

	// use real local file for caching
	private static java.io.File cacheRoot() {
		//java.io.File cache = FileSystem.getFileSystem().getCacheDirectory();
		// u = new File(cache, "peers").toRealURI();

		//return new java.io.File(u);
		return Environment.getDownloadCacheDirectory();
	}

	private static String readPeer(String peer, String cmd, java.io.File cache)
			throws Exception {
		Socket s = null;
		try {
			s = new PeerSocket(peer);
			PrintWriter w = new PrintWriter(s.getOutputStream());
			w.print(cmd);
			w.print(CRLF);
			w.flush();
			w.close();
			//
			InputStream in = s.getInputStream();
			CopyInputStream cin = new CopyInputStream(in,
					new java.io.FileOutputStream(cache));
			String r = read(new InputStreamReader(cin));
			if (!r.startsWith("2")) {
				if (r.startsWith("501")) {
					throw new FileNotFoundException(r);
				}
				throw new IOException(r);
			}
			return r;
		} catch (Exception e) {
			cache.delete();
			throw e;
		} finally {
			if (s != null) {
				s.close();
			}
		}
	}

	private static String read(Reader r) throws IOException {
		BufferedReader br = new BufferedReader(r, File.BUFSIZE);
		StringWriter sw = new StringWriter();
		String line = null;
		while (true) {
			line = br.readLine();
			if (line == null) {
				break;
			}
			sw.write(line);
			sw.write(CRLF);
		}
		br.close();
		return sw.toString();
	}

	private static String readCache(java.io.File f) throws IOException {
		if (f.exists() && f.canRead()) {
			return read(new java.io.FileReader(f));
		}
		return null;
	}

	/**
	 * Query file attributes
	 * 
	 * @param peer
	 * @param path
	 * @return
	 * @throws IOException
	 * @throws ParseException
	 */
	public static Info info(String peer, String path) throws Exception,
			ParseException {
		java.io.File cache = getCacheFile(peer, path, "info.txt");
		String s = null;
		s = readCache(cache);
		if (s == null) {
			s = readPeer(peer, "/bin/info " + path, cache);
		}
		Info fi = new Info();
		s = s.substring(4); // skip status code 200
		parse(s, fi);
		return fi;
	}

	private static java.io.File getCacheFile(String peer, String path,
			String filename) {
		java.io.File dir = new java.io.File(cacheRoot, peer + path);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return new java.io.File(dir, filename);
	}

	/**
	 * Read first 10 lines of a file
	 * 
	 * @param peer
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static String head(String peer, String path) throws Exception {
		java.io.File cache = getCacheFile(peer, path, "head.txt");
		String s = null;
		s = readCache(cache);
		if (s == null) {
			s = readPeer(peer, "/bin/head " + path, cache);
		}
		return s;
	}

	/**
	 * Download file from peer site
	 * 
	 * @param peer
	 * @param path
	 * @param local
	 * @throws Exception
	 */
	public static InputStream pullAsStream(String peer, String path)
			throws Exception {
		java.io.File cache = getCacheFile(peer, path, "pull.bin");
		if (cache.exists()) {
			return new java.io.FileInputStream(cache);
		}
		try {
			final Socket s = new PeerSocket(peer);
			PrintWriter w = new PrintWriter(s.getOutputStream());
			w.print("/bin/pull " + path + "\r\n");
			w.flush();
			w.close();
			final InputStream in = s.getInputStream();
			final CopyInputStream cin = new CopyInputStream(in,
					new java.io.FileOutputStream(cache));
			return new FilterInputStream(cin) {
				@Override
				public void close() throws IOException {
					super.close();
					s.close();
				}
			};
		} catch (Exception e) {
			cache.delete();
			throw e;
		} finally {
			//
		}
	}

	public static void pull(String peer, String path, String local)
			throws Exception {
		Socket s = null;
		try {
			s = new PeerSocket(peer);
			PrintWriter w = new PrintWriter(s.getOutputStream());
			w.print("/bin/pull " + path + "\r\n");
			w.flush();
			w.close();
			InputStream in = s.getInputStream();
			OutputStream out = new FileOutputStream(local);
			byte[] buf = new byte[File.BUFSIZE];
			int count = 0;
			while (true) {
				int read = in.read(buf);
				if (read == -1) {
					break;
				}
				if (read > 0) {
					count += read;
					out.write(buf, 0, read);
				}
			}
			out.close();
			in.close();
		} finally {
			if (s != null) {
				s.close();
			}
		}
	}

	public static void thumbnail(String peer, String path, String local)
			throws Exception {
		Socket s = null;
		try {
			s = new PeerSocket(peer);
			PrintWriter w = new PrintWriter(s.getOutputStream());
			w.print("/bin/thumbnail " + path + "\r\n");
			w.flush();
			w.close();
			InputStream in = s.getInputStream();
			OutputStream out = new FileOutputStream(local);
			byte[] buf = new byte[File.BUFSIZE];
			int count = 0;
			while (true) {
				int read = in.read(buf);
				if (read == -1) {
					break;
				}
				if (read > 0) {
					count += read;
					out.write(buf, 0, read);
				}
			}
			out.close();
			in.close();
		} finally {
			if (s != null) {
				s.close();
			}
		}
	}

	public static int list(String peer, String path, int offset, int max,
			List<Info> list) throws Exception {
		java.io.File cache = getCacheFile(peer, path, "list." + offset + "."
				+ max + ".txt");
		Socket s = null;
		try {
			BufferedReader br = null;
			if (cache.exists()) {
				br = new BufferedReader(new java.io.FileReader(cache));
			} else {
				s = new PeerSocket(peer);
				PrintWriter w = new PrintWriter(s.getOutputStream());
				offset = (offset < 0) ? 0 : offset;
				max = (max < 0) ? Integer.MAX_VALUE : max;
				w
						.print("/bin/list " + path + " " + offset + " " + max
								+ "\r\n");
				w.flush();
				w.close();
				InputStream in = s.getInputStream();
				CopyInputStream cin = new CopyInputStream(in,
						new java.io.FileOutputStream(cache));
				br = new BufferedReader(new InputStreamReader(cin),
						File.BUFSIZE);
			}
			String line = null;
			int cnt = 0;
			String status = null;
			String total = null;
			while (true) {
				line = br.readLine();
				if (line == null) {
					break;
				}
				switch (++cnt) {
				case 1:
					status = line;
					if (!status.startsWith("2")) {
						throw new IOException(status);
					}
					break;
				case 2:
				default:
					if (line.startsWith("Total:")) {
						total = line;
						break;
					}
					Info fi = new Info();
					// ignore blank lines
					line = line.trim();
					if (line.length() > 0) {
						parse(line, fi);

						list.add(fi);
					}
				}
			}

			br.close();

			return Integer.parseInt(total.substring(total.lastIndexOf(":") + 1)
					.trim());
		} catch (Exception e) {
			cache.delete();
			throw e;
		} finally {
			if (s != null) {
				s.close();
			}
		}
	}
	
	private static void parse(String s, Info fi) {
		// TODO Auto-generated method stub
		throw new RuntimeException("TBD");
	}

	public static boolean supportsShell(String peer) {
		return ChannelManager.getInstance().ping(peer);
	}

}
